import {isObject,isArray,checkData,createRes} from '../utils';
const indexedDB = window.indexedDB;
const logError = (str: any) => {
  console.error(`%c${str}`, "color:red")
};

class Dep {
  private deps: Array < any >
  constructor() {
    this.deps = [];
  }
  add(element) {
    this.deps.push(element);
  }
  notify() {
    for (let i = 0; i < this.deps.length; i++) {
      this.deps[i]();
    }
    this.deps.length = 0;
  }
}
class DB {
  public dbName: string;
  public version: number;
  public db: any;
  private idb: any;
  public table: Array < any > ;
  private _dep_: Dep;
  constructor({dbName,version}) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
    this.idb = null;
    this.table = [];
    this._dep_ = new Dep();
  }
  /**
   * 打开数据库
   * @success 成功的回调，返回db，非必传
   * @error 失败的回调，返回错误信息，非必传
   * */
  open() {
    return new Promise(resolve => {
      const request = indexedDB.open(this.dbName, this.version);
      request.onerror = (e: any) => {
        logError(e.currentTarget.error.message);
      };
      request.onsuccess = (e: any) => {
        this.db = e.target.result;
        resolve(createRes(true, this.db));
        this._dep_.notify();
      };
      request.onupgradeneeded = (e: any) => {
        this.idb = e.target.result;
        for (let i = 0; i < this.table.length; i++) {
          this._createTable(this.idb, this.table[i]);
        }
      };
    })
  }
  //  关闭数据库
  closeDB() {
    const handler = () => {
      this.db.close();
    };
    this._action(handler);
  }
  //清空某张表的数据
  clearTable({tableName}) {
    return new Promise((resolve,reject)=>{
      const handler = () =>{
        try {
          this._createTransaction(tableName, "readwrite").clear()
          resolve(createRes(true))
        } catch (error) {
          resolve(createRes(error))
        } 
      }
      this._action(handler);
    })
  }
  /**
   * @method 查询
   * @param {Object}
   *   @property {String} tableName 表名
   *   @property {Function} condition 查询的条件
   *      @arg {Object} 遍历每条数据，和filter类似
   *      @return 条件
   * */
  query({tableName,condition}) {
    if (typeof condition !== "function") {
      logError("condition必须是一个函数");
      return;
    }
    return new Promise((resolve) => {
      const handler = () => {
        let res: Array < any > = [];
        this._createTransaction(tableName, "readonly").openCursor().onsuccess = e =>{
          this._cursorSuccess(e, {
            condition,
            handler: ({currentValue}) => res.push(currentValue),
            over: () => resolve(createRes(true, res))
          });
        }  
      };
      this._action(handler);
    })
  }

  /**
   * @method 增加数据
   * @param {Object}
   *   @property {String} tableName 表名
   *   @property {Object} data 插入的数据
   * */
  insert({tableName,data},force=false) {
    return new Promise(resolve => {
      let nowData = checkData(data, this._getTemplate(tableName));
      if (nowData.status != 200) {
        resolve(nowData)
        return;
      }
      let errorArr: Array < any > = [],
        num = 0;
      let responseBack = function () {
        num++;
        if (num < nowData.result.length) return;

        if (errorArr.length > 0) {
          resolve(createRes(errorArr.join(';')));
        } else {
          resolve(createRes(true, nowData.result))
        }
      };
      this._action(() => {
        const store = this._createTransaction(tableName, "readwrite");
        nowData.result.forEach(v => {
          let request = force?store.put(v):store.add(v);
          request.onerror = (e) => {
            errorArr.push(e.target.error.message);
            responseBack()
          };
          request.onsuccess = () => {
            responseBack()
          }
        });
      });
    })
  }
  /**
   * @method 修改某条数据(主键)
   * @param {Object}
   *   @property {String} tableName 表名
   *   @property {Object\|Array} data 要更新的数据
   * */
  update({tableName,data}) {
    let keyPath = this._getKeyName(tableName);
    let dataList = isArray(data) ? data : [data]
    return new Promise((resolve) => {
      let errorArr: Array < any > = [],
        num = 0;
      let responseBack = function () {
        num++;
        if (num < dataList.length) return;
        if (errorArr.length > 0) {
          resolve(createRes(errorArr.join(';')));
        } else {
          resolve(createRes(true, data));
        }
      };
      this._action(() => {
        const store = this._createTransaction(tableName, "readwrite");
        dataList.forEach(value => {
          let request = store.get(value[keyPath]);
          request.onsuccess = e => {
            let currentValue = e.target.result;
            currentValue = {
              ...currentValue,
              ...value
            };
            if (!e.target.result) {
              errorArr.push(`${value[keyPath]}数据不存在`);
            } else {
              let nowData = checkData(currentValue, this._getTemplate(tableName));
              if (nowData.status != 200) {
                errorArr.push(nowData.message)
              } else {
                store.put(currentValue);
                value = currentValue;
              }
            }
            responseBack();
          };
          request.onerror = e => {
            errorArr.push(e.target.error.message);
            responseBack()
          }
        })
      });
    })
  }
  /**
   * @method 删除数据(主键)
   * @param {Object}
   *   @property {String} tableName 表名
   *   @property {String|Number|Array} target 目标主键值
   * */
  delete({tableName,target}) {
    let deleteList = [target];
    if (isArray(target)) {
      deleteList = target;
    }
    return new Promise((resolve) => {
      let errorArr: Array < any > = [],
        num = 0;
      let responseBack = function () {
        num++;
        if (num < deleteList.length) return;
        if (errorArr.length > 0) {
          resolve(createRes(errorArr.join(';')));
        } else {
          resolve(createRes(true, '删除成功'))
        }
      };
      this._action(() => {
        const store = this._createTransaction(tableName, "readwrite");
        deleteList.forEach(del => {
          let request = store.delete(del);
          request.onsuccess = (e) => {
            responseBack();
          };
          request.onerror = (e) => {
            errorArr.push(e.target.error.message);
            responseBack()
          };
        })
        if(deleteList.length == 0){
          responseBack()
        }
      });
    })
  }
  /**
   * @method 查询数据（主键值）
   * @param {Object}
   *   @property {String} tableName 表名
   *   @property {Number|String} target 主键值
   *
   * */
  queryByKey({tableName,target}) {
    return new Promise(resolve => {
      const handleFn = () => {
        let request = this._createTransaction(tableName, "readonly").get(target);
        request.onsuccess = e => {
          const result = e.target.result;
          if (!request) {
            resolve(createRes('未找到此数据'));
          } else {
            resolve(createRes(true, result || null));
          }
        };
        request.onerror = e => {
          resolve(createRes('未找到此数据'));
        }
      };
      this._action(handleFn);
    })
  }
  /**
   * @method 游标开启成功,遍历游标
   * @param {Function} 条件
   * @param {Function} 满足条件的处理方式 @arg {Object} @property cursor游标 @property currentValue当前值
   * @param {Function} 游标遍历完执行的方法
   * @return {Null}
   * */
  private _cursorSuccess(e, {condition,handler,over}) {
    const cursor = e.target.result;
    if (cursor) {
      const currentValue = cursor.value;
      if (condition(currentValue)) handler({
        cursor,
        currentValue
      });
      cursor.continue();
    } else {
      over();
    }
  }
  /**
   * 
   * @param {String} tableName 
   * @returns keyPathName
   */
  private _getKeyName(tableName) {
    let echo = '';
    this.table.forEach(table => {
      if (table.tableName === tableName) {
        echo = table.keyPath;
      }
    });
    return echo || '';
  }
  /**
   * 
   * @param {String} tableName 
   * @returns keyPathName
   */
  private _getTemplate(tableName) {
    let echo;
    this.table.forEach(table => {
      if (table.tableName === tableName) {
        echo = table.codes;
      }
    });
    return echo;
  }
  /**
   * @method 获取数据表
   * @param {String} 表名
   * @param {String} 权限
   * @return store
   * */
  private _createTransaction(tableName, mode = "readwrite") {
    if (!tableName || !mode) {
      throw new Error("请选择表名");
    }
    const transaction = this.db.transaction(tableName, mode);
    return transaction.objectStore(tableName);
  }

  // db是异步的,保证fn执行的时候db存在
  private _action(handler) {
    const action = () => {
      handler();
    };
    // 如果db不存在，加入依赖
    if (!this.db) {
      this._dep_.add(action);
      this.open();
    } else {
      action();
    }
  }

  /**
   * 创建table
   * @option<Object>  keyPath指定主键
   * @index 索引配置
   * */
  private _createTable(idb, {tableName,option,keyPath,indexs = []}) {
    if (!idb.objectStoreNames.contains(tableName)) {
      let store = idb.createObjectStore(tableName, {
        ...option,
        keyPath
      });
      for (let indexItem of indexs) {
        this._createIndex(store, indexItem);
      }
    }
  }

  /**
   * 创建索引
   * @option<Object> unique是否是唯一值
   * */
  private _createIndex(store, {key, option}) {
    store.createIndex(key, key, option);
  }
}

export default DB;